package com.didispace.web;

import com.didispace.hystrix.User;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;

@Service
public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    // 虽然 @HystrixCommand 注解可以非常优雅地定义 Hystrix 命令的实现，但是如上定义的 getUserById 方式只是同步执行的实现，
    // 若要实现异步执行则还需另外定义，比如:  p152
    @HystrixCommand
    public Future<User> getUserByIdAsync(final String id) {
        return new AsyncResult<User>() {
            @Override
            public User invoke() {
                return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id);
            }
        };
    }

    // @HystrixCommand
    @HystrixCommand(fallbackMethod = "defaultUser" )    // p155  定义服务降级  若要通过注解实现服务降级只需要使用 @HystrixCommand 中的 fallbackMethod 参数来指定具体的服务降级实现方法
    public User getUserById(Long id) {
        Map<String, String> params = new HashMap<String, String>();
        // params.put("id", String.valueOf(id));
        params.put("name", "Garbo");
        params.put("age", "26");
        return restTemplate.getForObject("http://USER-SERVICE/user4", User.class, params);
    }

    // public User defaultUser(Long id) {
    //     return new User(id);
    // }

//     public User defaultUser() {
//         return new User();
//     }
// 2023-02-10 20:00:18.065 ERROR 26752 --- [nio-9000-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
// [Request processing failed; nested exception is com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException: fallback method wasn't found: defaultUser([class java.lang.Long])]

    @HystrixCommand(fallbackMethod = "defaultUserSec" )
    public User defaultUser(Long id) {
        // 此处可能是另外一个网络请求来荻取，所以也有可能失败
        return new User("First Fallback" );
    }

    public User defaultUserSec(Long id) {
        return new User("Second Fallback" );
    }

    // 第5章  服务容错保护： Spring Cloud Hystrix -- 使用详解 -- 异常处理 -- 异常传播  p157
    // 当 getUserById 方法抛出了类型为 BadRequestException 的异常时，Hystrix 会将它包装在 HystrixBadRequestException 中抛出，
    // 这样就不会触发后续的 fallback 逻辑
    // @HystrixCommand(ignoreExceptions = {BadRequestException.class})
    @HystrixCommand(ignoreExceptions = {InterruptedException.class})
    public User getUserById2(Long id) {    //睡眠3秒，让回调生效
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id);
    }
    // http://localhost:9000/InterruptedEException/1
    // java.lang.InterruptedException: sleep interrupted
    // at java.lang.Thread.sleep(Native Method)
    // at com.didispace.web.UserService.getUserById2(UserService.java:72)
    // at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    // at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    // at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    // at java.lang.reflect.Method.invoke(Method.java:498)

    // 第5章  服务容错保护： Spring Cloud Hystrix -- 使用详解 -- 异常处理 -- 异常获取  p157
    @HystrixCommand(fallbackMethod = "fallback1" )
    public User getUserById3(String id) {
        throw new RuntimeException("getUserByid command failed" );
    }

    // User fallbackl{String id, Throwable e) {
    void fallback1(String id, Throwable e) {
        assert "getUserByid command failed".equals(e.getMessage());
    }
    // http://localhost:9000/CapturedEException/1


    // 第5章  服务容错保护： Spring Cloud Hystrix -- 使用详解 -- 异常处理 -- 命令名称、分组以及线程池划分  p158
    // public UserCommand() {
    //     super(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GroupName"))
    //             .andCommandKey(HystrixCommandKey.Factory.asKey("ConunandName")););
    // }
    //
    // public UserCommand() {
    //     super(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey
    //             ("CommandGroupKey"))
    //             .andCommandKey(HystrixCommandKey.Factory.asKey("CommandKey"))
    //             .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("ThreadPoolKey")));
    // }
}
